﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Common;
using ImageProcessing;
using ImageProcessing.Engine;
using ImageProcessing.Storage;
using Microsoft.Phone.Controls;
using System.Threading;
using Microsoft.Devices;
using System.Windows.Media.Imaging;
using ImageProcessing.Common;
using System.Windows.Controls;
using WcfClient;
using wp7example.Overlay;

namespace wp7example
{
    public partial class MainPage : PhoneApplicationPage
    {
        private PhotoCamera _cam = new PhotoCamera();
        private WriteableBitmap _overlayBitmap;

        private const bool ShowInterestPoints = true;
        private const bool EnableShowDebugStatus = true;

        private readonly Color _magentaPen = Colors.Magenta;
        private readonly Color _bluePen = Colors.Blue;
        private readonly Color _matchedPen = Colors.Red;
        private readonly Color _processedPen = Colors.Yellow;

        private ImageProcessingEngine _ipe;
        private ManualResetEvent _uiSyncEvent;

        private IpeRemoteLookup IpeRemoteLookup = IpeRemoteLookup.LocalHost;
        private IRemoteLookupService _iRemoteLookupService;

        bool _camIntialized;

        private Grid _overlayGrid;

        private bool _boolFoundObjectsShowing;
        private string _foundObjectsShowingName;
        private int _failCount;
        private const int FailMaxCount = 3;

        # region intilizers and shutdown

        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        //Code for camera initialization event, and setting the source for the viewfinder
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {

            // Check to see if the camera is available on the device.
            if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
            {
                // Initialize the default camera.
                _cam = new PhotoCamera(CameraType.Primary);

                //Event is fired when the PhotoCamera object has been initialized
                _cam.Initialized += CameraInitialized;

                //Set the VideoBrush source to the camera
                viewfinderBrush.SetSource(_cam);
            }
            else
            {
                UpdateStatus("A Camera is not available on this device.", txtStatus);
            }
        }

        protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            _camIntialized = false;

            if (_cam == null)
                return;

            // Dispose of the camera to minimize power consumption and to expedite shutdown.
            _cam.Dispose();

            // Release memory, ensure garbage collection.
            _cam.Initialized -= CameraInitialized;

            CleanupServerConnection();

            if (_iRemoteLookupService != null)
            {
                _iRemoteLookupService.HBWaitngForResponse -= HBWaiting;
                _iRemoteLookupService.ServerOnline -= ServerOnline;
            }

            CleanupIPE();
        }

        private void CleanupServerConnection()
        {
            if (_iRemoteLookupService != null)
                _iRemoteLookupService.Dispose();
        }

        //Update UI if initialization succeeds
        private void CameraInitialized(object sender, CameraOperationCompletedEventArgs e)
        {
            if (e.Succeeded)
            {
                var width = (int)_cam.PreviewResolution.Width;
                var height = (int)_cam.PreviewResolution.Height;

                _camIntialized = true;

                Dispatcher.BeginInvoke(() =>
                                           {
                                               _overlayBitmap = new WriteableBitmap(width, height);
                                               overlayImage.Source = _overlayBitmap;
                                           });

                CleanupIPE();
                _uiSyncEvent = new ManualResetEvent(true);

                _ipe = new ImageProcessingEngine(width, height,
                    _cam.GetPreviewBufferArgb32,
                    _cam.GetPreviewBufferY,
                    _uiSyncEvent);

                _ipe.LearnComplete += LearnCompleted;
                _ipe.DescriptorsUpdate += DescriptorUpdate;
                _ipe.StatusUpdater += UpdateDebugStatus;

                LoadPointsFromDB();

                _ipe.SaveAddedImagesLocaly = true;
                _ipe.ImageArchiver = new Wp7ImageArchiver();

                _ipe.Wp7OrtProxy = _iRemoteLookupService = SimpleRemoteLookupFactory(IpeRemoteLookup);
                _iRemoteLookupService.HBWaitngForResponse += HBWaiting;
                _iRemoteLookupService.ServerOnline += ServerOnline;

                _ipe.Start();
            }
        }

        private void ServerOnline(bool isOnline)
        {
            UpdateStatus("Server is online : " + isOnline, txtOtherStatus);

            imgCloudOnline.Dispatcher.BeginInvoke(() =>
                                                      {
                                                          imgCloudOnline.Visibility = isOnline
                                                                                          ? Visibility.Visible
                                                                                          : Visibility.Collapsed;
                                                      });
        }

        private void HBWaiting(bool isWaiting)
        {
            imgCloudTalking.Dispatcher.BeginInvoke(() =>
                                                       {
                                                           imgCloudTalking.Visibility = isWaiting
                                                                                          ? Visibility.Visible
                                                                                          : Visibility.Collapsed;
                                                       });
        }

        private void LoadPointsFromDB()
        {
            UpdateStatus("Loading points from DB", txtOtherStatus);

            Stopwatch sw = new Stopwatch();
            sw.Start();
            _ipe.DBConnection = new WP7PersistanceDB();
            int pointsCount = _ipe.LoadAllPointsFromDB();
            sw.Stop();
            string loadTime = string.Format("Loaded {0} points in {1} ms", pointsCount, sw.ElapsedMilliseconds);

            UpdateStatus(loadTime, txtOtherStatus);
        }

        private static IRemoteLookupService SimpleRemoteLookupFactory(IpeRemoteLookup ipeRemoteLookup)
        {
            switch (ipeRemoteLookup)
            {
                case IpeRemoteLookup.LocalHost:
                case IpeRemoteLookup.RemoteShared:
                    return new Wp7OrtProxy(ipeRemoteLookup);
                    break;

                case IpeRemoteLookup.Disabled:
                    return null;
                    break;

                default:
                    throw new NotImplementedException();
            }
        }

        private void LearnCompleted()
        {
            UpdateStatus("Finished learning....", txtStatus);
            btnSave.Dispatcher.BeginInvoke(() => btnSave.IsEnabled = true);
        }

        #endregion

        #region UI Updates

        private void ShowOverLayTest(bool wasFound)
        {
            if (!wasFound && _overlayGrid != null && ContentPanel.Children.Contains(_overlayGrid))
            {
                ContentPanel.Children.Remove(_overlayGrid);
                _overlayGrid = null;
            }

            if (wasFound)
            {
                if (_overlayGrid == null)
                {
                    _overlayGrid = OverlayPlayer.LoadXAMLTest();
                    ContentPanel.Children.Add(_overlayGrid);
                }

                _overlayGrid.Margin = new Thickness(_overlayGrid.Margin.Bottom + 10d);
            }


        }


        private void DescriptorUpdate(ImageScanResultsDTO imdr)
        {
            FoundObjectsUpdate(imdr);

            Deployment.Current.Dispatcher.BeginInvoke(delegate
                                                          {
                                                              if (ShowInterestPoints)
                                                                  UpdateOverlayBitmap(imdr.InterestPoints);


                                                              UpdateStatus(
                                                                  " F : " + imdr.IptsCount
                                                                  + " P : " + imdr.PIptsCount
                                                                  + " M : " + imdr.MatchCount
                                                                  , txtPoints
                                                                  );

                                                              UpdateStatus(
                                                                  " C : " + imdr.CaptureMs
                                                                  + " S : " + imdr.SelecterMs
                                                                  + " D : " + imdr.DescriptorMs
                                                                  + " M : " + imdr.MatcherMs
                                                                  , txtTime
                                                                  );

                                                              // allow start new pass
                                                              _uiSyncEvent.Set();

                                                          });

        }

        private void FoundObjectsUpdate(ImageScanResultsDTO imageScanResultsDto)
        {
            bool imageHasBeenFound = false;

            if (imageScanResultsDto.ObjectFound && imageScanResultsDto.MatchCount >= _ipe.MinFoundPoints)
            {
                _failCount = 0;
                imageHasBeenFound = true;
            }
            else
            {
                _failCount++;
                if (_failCount < FailMaxCount)
                    return;
            }

            if (imageHasBeenFound == _boolFoundObjectsShowing &&
                _foundObjectsShowingName == imageScanResultsDto.BestFoundName)
                return;

            _boolFoundObjectsShowing = imageHasBeenFound;
            _foundObjectsShowingName = imageHasBeenFound ? imageScanResultsDto.BestFoundName : null;

            var msg = _boolFoundObjectsShowing ? "Found : " + imageScanResultsDto.BestFoundName : ImageProcessingEngine.Scanning;
            UpdateStatus(msg, txtStatus);
            Deployment.Current.Dispatcher.BeginInvoke(() => ShowOverLayTest(_boolFoundObjectsShowing));
        }

        private void UpdateOverlayBitmap(List<InterestPoint> interestPoints)
        {
            if (!_camIntialized)
                return;

            _overlayBitmap.Clear();

            if (interestPoints.FirstOrDefault() != null)
                ShowInternestPoints(_overlayBitmap, interestPoints);

            _overlayBitmap.Invalidate();
        }

        private void UpdateStatus(string text, TextBlock tbox)
        {
            if (!EnableShowDebugStatus)
                return;

            if (Dispatcher.CheckAccess())
                ShowDebugStatus(text, tbox);
            else
                Dispatcher.BeginInvoke(() => ShowDebugStatus(text, tbox));
        }

        private void ShowDebugStatus(string text, TextBlock tbox)
        {
            tbox.Text = text;
        }

        private void ShowInternestPoints(WriteableBitmap img, IEnumerable<InterestPoint> ipts)
        {
            foreach (var ip in ipts)
            {
                var s = 2 * Convert.ToInt32(2.5f * ip.Scale);
                var r = Convert.ToInt32(s / 2f);

                Color myPen;

                if (ip.Matched)
                    myPen = _matchedPen;
                else if (ip.Processed)
                    myPen = _processedPen;
                else
                {
                    myPen = (ip.Sign > 0 ? _bluePen : _magentaPen);
                }

                img.DrawEllipse((ip.X - r), (ip.Y - r), ip.X, ip.Y, myPen);
            }
        }

        #endregion

        #region button handlers

        private void BtnLearnClick(object sender, RoutedEventArgs e)
        {
            UpdateStatus("Learning....", txtStatus);
            _ipe.Learn();

            ObjectName.Text = string.Empty;
            ShowSave.Visibility = Visibility.Visible;
            btnSave.IsEnabled = false;
        }

        private void BtnSaveClick(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o =>
                                       {
                                           UpdateStatus("Learning....", txtStatus);

                                           ManualResetEvent mre = new ManualResetEvent(false);

                                           string tagName = null;
                                           Guid tagId = Guid.NewGuid();

                                           Dispatcher.BeginInvoke(() =>
                                                                      {
                                                                          ShowSave.Visibility = Visibility.Collapsed;
                                                                          tagName = ObjectName.Text;
                                                                          mre.Set();

                                                                      });

                                           mre.WaitOne();

                                           _ipe.SaveLearnt(new TagInfo(tagId, tagName));
                                           UpdateStatus(ImageProcessingEngine.Scanning, txtStatus);
                                       });
        }

        #endregion

        private void UpdateDebugStatus(string status)
        {
            UpdateStatus(status, txtStatus);
        }

        private void CleanupIPE()
        {
            if (_uiSyncEvent != null)
                _uiSyncEvent.Dispose();

            if (_ipe == null) return;

            _ipe.LearnComplete -= LearnCompleted;
            _ipe.DescriptorsUpdate -= DescriptorUpdate;
            _ipe.StatusUpdater -= UpdateDebugStatus;

            _ipe.Dispose();
        }

        private void Purge(object sender, EventArgs e)
        {
            _ipe.PurgeAllData();
        }
    }
}